
// XortStudioDoc.cpp : implementation of the CXortStudioDoc class
//

#include "stdafx.h"
// SHARED_HANDLERS can be defined in an ATL project implementing preview, thumbnail
// and search filter handlers and allows sharing of document code with that project.
#ifndef SHARED_HANDLERS
#include "XortStudio.h"
#endif

#include "XortStudioDoc.h"
#include "CntrItem.h"

#include <propkey.h>

#ifdef _DEBUG
#define new DEBUG_NEW
#endif

// CXortStudioDoc

IMPLEMENT_DYNCREATE(CXortStudioDoc, CRichEditDoc)

BEGIN_MESSAGE_MAP(CXortStudioDoc, CRichEditDoc)
	// Enable default OLE container implementation
	ON_UPDATE_COMMAND_UI(ID_OLE_EDIT_LINKS, &CRichEditDoc::OnUpdateEditLinksMenu)
	ON_COMMAND(ID_OLE_EDIT_LINKS, &CRichEditDoc::OnEditLinks)
	ON_UPDATE_COMMAND_UI_RANGE(ID_OLE_VERB_FIRST, ID_OLE_VERB_LAST, &CRichEditDoc::OnUpdateObjectVerbMenu)
END_MESSAGE_MAP()


// CXortStudioDoc construction/destruction

CXortStudioDoc::CXortStudioDoc()
{
	m_iArgc = 0;
    m_pArgv = NULL;
    m_pDebugThread = NULL;
    m_eState = XVM_Idle;
    m_NextStepKind = XVM_Step_Over;
}

CXortStudioDoc::~CXortStudioDoc()
{
    if( m_pDebugThread )
    {
        setState( XVM_Terminating );
        WaitForSingleObject( m_pDebugThread->m_hThread, INFINITE );
    }
}
    
BOOL CXortStudioDoc::OnNewDocument()
{
	if (!CRichEditDoc::OnNewDocument())
		return FALSE;

	// TODO: add reinitialization code here
	// (SDI documents will reuse this document)

	return TRUE;
}

BOOL CXortStudioDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
    if( !CRichEditDoc::OnOpenDocument( lpszPathName ) )
        return FALSE;

    BOOL bRet = FALSE;
    m_csFileName = lpszPathName;
    if( XORT_OK == LoadExecutable( m_csFileName, m_listGlobal, m_litPool ) )
    {
        string csLogFile = lpszPathName;
        csLogFile += ".log";
        io_logOpen( csLogFile );
        xInitPrims();

        SymbolRef mainEntry = FindSymbol( m_listGlobal, "main", TRUE );
        if( mainEntry.isNULL() )
        {
            AfxMessageBox( "entry point not found" );
        } else {

            int iRet = m_runTime.Initialize( m_listGlobal, m_litPool, static_cast<XRuntimeDebugEvents*>( this ), false );
            if( iRet != XORT_OK )
            {
                CString csFmt;
                csFmt.Format( "Runtime initialization error: '%d'", iRet );
                AfxMessageBox( csFmt );
                GetOutput()->Message( OutputBuild, csFmt );
            } else {

                CDisassemblerRTF dasm( &m_runTime );
                dasm.Disassemble( m_csAsmRTF, m_arLines );

                GetClasses()->ShowXRTContent( m_csFileName.GetBuffer(), &m_runTime );

                GetMainView()->ShowDisassembly( m_csAsmRTF );
                
                GetOutput()->Message( OutputBuild, "File '%s' loaded...", m_csFileName.GetBuffer() );

                TRACE( "Number of lines in array: %d\n", m_arLines.count() );

                m_pDebugThread = AfxBeginThread( &DebugThreadProcStatic, this );

                bRet = TRUE;
            }
        }

        if( !bRet )
        {
            m_listGlobal.clear();
            m_litPool.clear();
            m_csFileName.Empty();
            xFreePrims();
            io_logClose();
        }
    }

    SetModifiedFlag( FALSE );
    return bRet;
}

BOOL CXortStudioDoc::OnSaveDocument(LPCTSTR lpszPathName)
{
    if( !CRichEditDoc::OnSaveDocument( lpszPathName ) )
        return FALSE;

    return TRUE;
}

void CXortStudioDoc::OnCloseDocument()
{
    xFreePrims();
    m_litPool.clear();
    m_listGlobal.clear();
    io_logClose();
    
    CRichEditDoc::OnCloseDocument();
}

CRichEditCntrItem* CXortStudioDoc::CreateClientItem(REOBJECT* preo) const
{
	return new CXortStudioCntrItem(preo, const_cast<CXortStudioDoc*>(this));
}


// CXortStudioDoc serialization

void CXortStudioDoc::Serialize(CArchive& ar)
{
	if (ar.IsStoring())
	{
		// TODO: add storing code here
	}
	else
	{
		// TODO: add loading code here
	}

	// Calling the base class CRichEditDoc enables serialization
	//  of the container document's COleClientItem objects.
	// TODO: set CRichEditDoc::m_bRTF = FALSE if you are serializing as text
	CRichEditDoc::Serialize(ar);
}

#ifdef SHARED_HANDLERS

// Support for thumbnails
void CXortStudioDoc::OnDrawThumbnail(CDC& dc, LPRECT lprcBounds)
{
	// Modify this code to draw the document's data
	dc.FillSolidRect(lprcBounds, RGB(255, 255, 255));

	CString strText = _T("TODO: implement thumbnail drawing here");
	LOGFONT lf;

	CFont* pDefaultGUIFont = CFont::FromHandle((HFONT) GetStockObject(DEFAULT_GUI_FONT));
	pDefaultGUIFont->GetLogFont(&lf);
	lf.lfHeight = 36;

	CFont fontDraw;
	fontDraw.CreateFontIndirect(&lf);

	CFont* pOldFont = dc.SelectObject(&fontDraw);
	dc.DrawText(strText, lprcBounds, DT_CENTER | DT_WORDBREAK);
	dc.SelectObject(pOldFont);
}

// Support for Search Handlers
void CXortStudioDoc::InitializeSearchContent()
{
	CString strSearchContent;
	// Set search contents from document's data. 
	// The content parts should be separated by ";"

	// For example:  strSearchContent = _T("point;rectangle;circle;ole object;");
	SetSearchContent(strSearchContent);
}

void CXortStudioDoc::SetSearchContent(const CString& value)
{
	if (value.IsEmpty())
	{
		RemoveChunk(PKEY_Search_Contents.fmtid, PKEY_Search_Contents.pid);
	}
	else
	{
		CMFCFilterChunkValueImpl *pChunk = NULL;
		ATLTRY(pChunk = new CMFCFilterChunkValueImpl);
		if (pChunk != NULL)
		{
			pChunk->SetTextValue(PKEY_Search_Contents, value, CHUNK_TEXT);
			SetChunkValue(pChunk);
		}
	}
}

#endif // SHARED_HANDLERS

// CXortStudioDoc diagnostics

#ifdef _DEBUG
void CXortStudioDoc::AssertValid() const
{
	CRichEditDoc::AssertValid();
}

void CXortStudioDoc::Dump(CDumpContext& dc) const
{
	CRichEditDoc::Dump(dc);
}
#endif //_DEBUG


// CXortStudioDoc commands

//////////////////////////////////////////////////////////////////////////
//
void CXortStudioDoc::LocateSymbolInView( symbolref pSymbol )
{
    if( pSymbol->m_dwStorage == cSTORAGE_MEMBER )
    {
        for( DWORD i = 0; i < m_arLines.count(); i++ )
        {
            CDebugLine *pdbgLine = m_arLines.get( i );
            DbgLineType _lt = pdbgLine->GetLineType();

            if( _lt == dl_ObjectDeclaration )
            {
                if( pSymbol->m_pOwner->m_csName == pdbgLine->GetName() )
                {
                    TRACE( "Line owner number: %d\n", i );

                    for( DWORD x = i; x < m_arLines.count(); x++ )
                    {
                        CDebugLine *pdbgLineMember = m_arLines.get( x );
                        if( pdbgLineMember->GetLineType() == dl_LocalFunction || pdbgLineMember->GetLineType() == dl_LocalVariable ) {
                            if( pSymbol->m_csName == pdbgLineMember->GetName() ) {
                                TRACE( "Line number memeber: %d\n", x );
                                GetMainView()->ScrollIntoView( x );
                                break;
                            }
                        }
                    }

                    break;
                }
            } 
        }
    } else { // search for global symbol

        for( DWORD i = 0; i < m_arLines.count(); i++ )
        {
            CDebugLine *pdbgLine = m_arLines.get( i );
            DbgLineType _lt = pdbgLine->GetLineType();

            if( _lt == dl_Variable || _lt == dl_ObjectDeclaration || _lt == dl_Function )
            {
                if( pSymbol->m_csName == pdbgLine->GetName() )
                {
                    TRACE( "Line number: %d\n", i );
                    GetMainView()->ScrollIntoView( i );
                    break;
                }
            } 
        }
    }
}

BOOL CXortStudioDoc::SetBreakpoint( DWORD dwLineIdx )
{
    if( dwLineIdx >= m_arLines.count() )
        return FALSE;

    BOOL bRet = FALSE;

    CDebugLine *pdbgLine = m_arLines.get( dwLineIdx );
    if( pdbgLine->GetLineType() == dl_AsmCode )
    {
        pdbgLine->SetBreakpoint( !pdbgLine->HasBreakpoint() );
        bRet = pdbgLine->HasBreakpoint();

        DWORD dwIP = pdbgLine->GetSymbolIndex(); // for dl_AsmCode this property contains IP
        symbolref pProc = pdbgLine->GetFunction();
        ASSERT( pProc );

        if( bRet )
        {
            CComPtr<IBreakpoint> spBP;
            if( SUCCEEDED( CreateBreakpoint( &spBP ) ) ) 
            {
                spBP->SetLineIndex( dwLineIdx );
                spBP->SetIP( dwIP );
                spBP->SetActive( TRUE );
                pProc->SetBreakpoint( dwIP, spBP );

                GetBreakpoints()->AddBreakpoint( dwLineIdx, dwIP, pProc->m_csName.GetBuffer() );
            } else {
                bRet = FALSE;
            }
        } else {
            pProc->RemoveBreakpoint( dwIP );
            GetBreakpoints()->RemoveBreakpoint( dwLineIdx );
        }
    } 

    return bRet;
}

void CXortStudioDoc::LocateLineInView( DWORD dwLineIdx )
{
    GetMainView()->ScrollIntoView( dwLineIdx );
}

void CXortStudioDoc::WaitForDebugEvent()
{
    XVMState eCurretState = getState();
    for( ; ; )
    {
        Sleep( 50 );
        if( eCurretState != getState() )
            break;
    }
}

void CXortStudioDoc::DebugRun()
{
    if( getState() == XVM_Idle )
    {
        int iRet = ParseCmdLine( m_csCmdLine.GetBuffer(), &m_iArgc, &m_pArgv );
        m_csCmdLine.ReleaseBuffer();
        if( iRet != 0 ) {
            GetOutput()->Message( OutputDebug, "Can not parse command line" );
            return;
        }

        GetApp()->GetMainFrame()->GetOutputWnd()->ActivateTab( OutputDebug );
    }

    setState( XVM_Executing );
}

void CXortStudioDoc::DebugStop()
{
    setState( XVM_DoSingleStep );
}

void CXortStudioDoc::DebugStepInto()
{
    setState( XVM_LazyExecution );
    m_NextStepKind = XVM_Step_Into;
}

void CXortStudioDoc::DebugStepOver()
{
    setState( XVM_DoSingleStep );
    m_NextStepKind = XVM_Step_Over;
}

void CXortStudioDoc::ToggleBreakpoint()
{

}

UINT __cdecl CXortStudioDoc::DebugThreadProcStatic( LPVOID pParam )
{
    CXortStudioDoc *pThis = (CXortStudioDoc*)pParam;
    pThis->DebugThreadProc();
    return 0;
}

void CXortStudioDoc::DebugThreadProc()
{
    TRACE( "CXortStudioDoc::DebugThreadProc() -> ENTER\n" );
    int iRet = XORT_OK;

    while( getState() != XVM_Terminating )
    {
        switch( getState() )
        {
        case XVM_Idle: 
            Sleep( 100 ); // :)
            break;

        case XVM_Executing:
            {
                Sleep( 100 ); // wait IDE for complete switch to debug mode :)
                TRACE( "RUNTIME -> ENTER XVM!\n" );

                try
                {
                    SymbolRef mainEntry = FindSymbol( m_listGlobal, "main", TRUE );
                    iRet = m_runTime.Run( mainEntry, m_iArgc, m_pArgv );

                } catch ( int iError ) {
                    TRACE( "RUNTIME -> user termination request: %d\n", iError );
                }

                TRACE( "RUNTIME -> LEAVE XVM!\n" );

                if( getState() != XVM_Terminating ) {
                    setState( XVM_Idle ); // do nothing after that
                    GetApp()->GetMainFrame()->PostMessage( WM_PROGRAM_ENDED );
                    OnProgramExecutionEnd();
                    UpdateStackView();

                    m_runTime.Reset(); // reset XVM
                }
                break;
            }

        default:
            break;
        }
    }

    TRACE( "CXortStudioDoc::DebugThreadProc() -> EXIT\n" );
}

//XRuntimeDebugEvents implementation
int CXortStudioDoc::onEnterSystemFrame( XRuntime *pRT, SymbolRef CurrentSymbol )
{
    return XORT_OK;
}

int CXortStudioDoc::onLeaveSystemFrame( XRuntime *pRT, SymbolRef CurrentSymbol )
{
    return XORT_OK;
}

int CXortStudioDoc::onEnterFrame( XRuntime *pRT )
{
    return XORT_OK;
}

int CXortStudioDoc::onLeaveFrame( XRuntime *pRT )
{
    return XORT_OK;
}

int CXortStudioDoc::beforeExecPrimitive( XRuntime *pRT, const InstructionData& primInfo )
{
    symbolref pCurProc = pRT->get_CurrentProc();
    if( pCurProc )
    {
        IBreakpoint *pBP = (IBreakpoint *)pCurProc->GetBreakpoint( pRT->get_IP() );
        if( pBP && pBP->GetActive() )
        {
            setState( XVM_AtBreakpoint );
            pBP->Hit();

            // update stack view
            UpdateStackView();

            // update cursor position
            GetMainView()->MoveDebugCursorToLine( pBP->GetLineIndex() );
            
            WaitForDebugEvent();
        }
        else
        {
            if( getState() == XVM_DoSingleStep )
            {
                setState( XVM_CauseBreak );

                // update stack view
                UpdateStackView();

                // update cursor position
                DWORD dwLineIdx = pCurProc->GetLineFromIP( pRT->get_IP() );
                GetMainView()->MoveDebugCursorToLine( dwLineIdx );

                WaitForDebugEvent();

            }
            else if( getState() == XVM_LazyExecution )
            {
                UpdateStackView();
                
                DWORD dwLineIdx = pCurProc->GetLineFromIP( pRT->get_IP() );
                GetMainView()->MoveDebugCursorToLine( dwLineIdx );

                Sleep( 100 );
            }
        }
    }

    if( getState() == XVM_Terminating )
        throw ERR_XVM_TERMINATE;    

    return XORT_OK;
}

int CXortStudioDoc::afterExecPrimitive( XRuntime *pRT, const InstructionData& primInfo, int iLastError )
{
    return XORT_OK;
}

int CXortStudioDoc::beforeExecBuildinProc( XRuntime *pRT )
{
    return XORT_OK;
}

int CXortStudioDoc::afterExecBuildinProc( XRuntime *pRT, int iLastError )
{
    return XORT_OK;
}

int CXortStudioDoc::beforeExecNativeProc( XRuntime *pRT )
{
    return XORT_OK;
}

int CXortStudioDoc::afterExecNativeProc( XRuntime *pRT, int iLastError )
{
    return XORT_OK;
}

int CXortStudioDoc::internalXVMError( XRuntime *pRT, int iError )
{
    return XORT_OK;
}

void CXortStudioDoc::outputDebugMessage( LPCSTR lpszMessage )
{
    GetApp()->GetMainFrame()->GetOutputWnd()->Message( OutputDebug, "%s", lpszMessage );
}
//////////////////////////////////////////////////////////////////////////
//
void CXortStudioDoc::UpdateStackView()
{
    CStackWnd *pStack = GetApp()->GetMainFrame()->GetStackWnd();
    pStack->Clear();

    const XCELLSARRAY& stk = m_runTime.get_Stack();

    for( DWORD i = 0; i < stk.count(); i++ )
    {
        const XortElement& el = stk.get( i );

        pStack->AddCellInfo( el );
    }
}

void CXortStudioDoc::OnProgramExecutionEnd()
{
    GetMainView()->MoveDebugCursorToLine( -1 );
    GetApp()->GetMainFrame()->GetOutputWnd()->ActivateTab( OutputBuild );
}
